/* virtualmem.c:
 *
 * Author: Will Lamond
 *
 * A wrapper for memory which abstracts away the mapping of logical addresses
 * to physical addresses.
 *
 */

#include "virtualmem.h"
#include "memory.h"
int pf=0;

int initVMem(VMEMMOD **vm, char* brainpath)
{
	(*vm) = malloc(sizeof(VMEMMOD));
	if (!(*vm)) {
		printf("Memory allocation failure in initVMem.\n");
		return -1;
	}
	init_mem(&(((*vm)->mm)), brainpath);
	(*vm)->pFs=0;
	(*vm)->LRUpage = 1;
	(*vm)->LRUpid = 1;
	return 1;
}

int vmem_read(VMEMMOD *vm, size_t memLoc, char* box, int pid)
{
	int pageNum, offset, err = 0; //page number, offset, and error
	pageNum = memLoc / PAGESIZE; //compute the page number
	offset = memLoc % PAGESIZE; //compute the offset
	char validness=0;

	validness = vm->mm->pcb[pid - 1].pagetable->pchValidness[pageNum]; //get the valid/invalid flag for that page

	if (validness == INVALID) { //check to see if the page is invalid
		err = pageIn(vm, pageNum, pid);
		if (err < 0) {
			printf("Error paging in the required page.\n");
			return -1;
		}
	}
	//compute the physical address from the processes page table and offset.
	int baseaddr = vm->mm->pcb[pid - 1].pagetable->pnPageAddr[pageNum];
	baseaddr = baseaddr + offset;

	err = mem_read(vm->mm, baseaddr, box, pid);
	if (err < 0) {
		printf("Error reading memory in vmem_read: INVALID page.\n");
		return -1;
	}
	return 1;
}

int vmem_store(VMEMMOD *vm, size_t memLoc, char value[4], int pid)
{
	int pageNum, offset, err = 0; //page number, offset, and error
	pageNum = memLoc / PAGESIZE; //compute the page number
	offset = memLoc % PAGESIZE; //compute the offset
	char validness = 0;

	validness = vm->mm->pcb[pid - 1].pagetable->pchValidness[pageNum]; //get the valid/invalid flag for that page

	if (validness == INVALID) { //check to see if the page is invalid
		err = pageIn(vm, pageNum, pid);
		if (err < 0) {
			printf("Error paging in the required page.\n");
			return -1;
		}
	}
	//compute the physical address from the processes page table and offset.
	int baseaddr = vm->mm->pcb[pid - 1].pagetable->pnPageAddr[pageNum];
	baseaddr = baseaddr + offset;

	err = mem_store(vm->mm, baseaddr, value, pid);
	if (err < 0) {
		printf("Error reading memory in vmem_read: INVALID page.\n");
		return -1;
	}
	return 1;
}


int pageIn(VMEMMOD *vm, int pageNum, int pid)
{
	int i, err, baseaddr= -1, diskbase = 0;
	int limitaddr = PAGESIZE;
	char temp[4];
	DISKNODE tempnode;

	baseaddr = FindFirstFit(vm->mm->freeMem, 1); //find a single available page

	if (baseaddr < 0){
		pageOut(vm, vm->LRUpage, vm->LRUpid);
		baseaddr = FindFirstFit(vm->mm->freeMem, 1); //find a single available page
	}

	vm->LRUpage = pageNum;
	vm->LRUpid = pid;

	//limitaddr = baseaddrm * PAGESIZE;

	err = getPidPage(vm->mm->usedDisk, &tempnode, pid);
	if (err < 0) {
		printf("Error finding disk base address for process %d in pageIn.\n", pid);
		return -1;
	}

	diskbase = tempnode.base;
	if ((diskbase) > (tempnode.end)) {
		printf("Disk reference is out of bounds in pageIn for process %d\n", pid);
		return -1;
	}
#ifdef DEBUG
	printf("writing proc #%d page num #%d from location"
			" #%d on disk to location %d in ram\n",pid, pageNum, diskbase+pageNum, baseaddr);
#endif
	/* Copy one word at a time from the page in disk */
	for (i=0 ; i < PAGESIZE; i++) {
		memcpy(temp,(void*) &(vm->mm->diskBlock[diskbase+pageNum][i*4]), sizeof(char)*4);
		memcpy((void*) &(vm->mm->ramBlock[(baseaddr*PAGESIZE)+i]), temp, sizeof(char)*4);

	}

	/* Set the new processes page table entry */
	vm->mm->pcb[pid-1].pagetable->pchValidness[pageNum] = VALID;
	vm->mm->pcb[pid-1].pagetable->pnPageAddr[pageNum] = (baseaddr*PAGESIZE);
#ifdef DEBUG_FAULTS | DEBUG
	printf("##PAGE-IN##   ");
#endif
	removeNode(vm->mm->freeMem, baseaddr, baseaddr);
	return 0;
}

int pageOut(VMEMMOD *vm, int pageNum, int pid)
{
	int i = 0;
	int err = -1, diskbase, baseaddr;
	char temp[4];
	DISKNODE tempnode;

	err = getPidPage(vm->mm->usedDisk, &tempnode, pid);
	if( err  < 0){
		printf("Failed to find page info for pid %d in pageOut\n", pid);
		return (-1);
	}
	diskbase = tempnode.base;

	/* Base address of the page being swapped out of RAM */
	baseaddr = vm->mm->pcb[pid-1].pagetable->pnPageAddr[pageNum];
#ifdef DEBUG
	printf("writing proc #%d page num #%d to location"
			" #%d on disk from location %d in ram\n",pid, pageNum, diskbase+pageNum, baseaddr/PAGESIZE);
#endif
	/* Copy one word at a time from the page in disk */
	for (i=0 ; i < PAGESIZE; i++) {
		memcpy((void*) temp, (void*) &(vm->mm->ramBlock[(baseaddr)+i]), sizeof(char)*4);
		memcpy((void*) &(vm->mm->diskBlock[diskbase+pageNum][i*4]), (void*) temp, sizeof(char)*4);
	}
	vm->pFs++;
#ifdef DEBUG_FAULTS | DEBUG
	printf("##PAGE-OUT##  #%d   ",pf);
#endif
	addNode(vm->mm->freeMem, baseaddr/PAGESIZE, baseaddr/PAGESIZE);
	/* The previous process holding this page is now invalid since it is swapped out */
	vm->mm->pcb[pid-1].pagetable->pchValidness[pageNum] = INVALID;
	//printf("New base address %d, Old base address %d\n", baseaddr, vm->mm->pcb[pid-1].pagetable->pnPageAddr[pageNum]);
	return 0;
}


int getNumPages(VMEMMOD *vm, int pid) {
	int pages,size = 0;
	size = vm->mm->pcb[pid-1].limitReg-vm->mm->pcb[pid-1].baseReg;
	pages = size/PAGESIZE;
	if (size % PAGESIZE)
		pages++;
	return pages;
}
